#ifndef cathlibcpp_functional_H
#define cathlibcpp_functional_H

// File:       functional.h
// Author:     (c) Miles Sabin, 1996
// Purpose:    approximation to ANSI C++ function object templates


#ifndef cathlibcpp_bool_H
#include "bool.h"
#endif

#ifndef cathlibcpp_config_H
#include "config.h"
#endif


#ifdef PROBLEM_LONG_NAMES

// abbreviations to stop CFront choking on long names

#define unary_function             __uf
#define binary_function            __bf
#define pointer_to_unary_function  __puf
#define pointer_to_binary_function __pbf

#endif


// function object bases

template<class Arg, class Result>
struct unary_function
{
};

template<class Arg1, class Arg2, class Result>
struct binary_function
{
};


// arithmetic functions

template<class T>
struct plus : binary_function<T, T, T>
{
  plus() {}

  T operator()(T const& x, T const& y) const
    { return x + y; }
};

template<class T>
struct minus : binary_function<T, T, T>
{
  minus() {}

  T operator()(T const& x, T const& y) const
    { return x - y; }
};

template<class T>
struct multiplies : binary_function<T, T, T>
{
  multiplies() {}

  T operator()(T const& x, T const& y) const
    { return x * y; }
};

template<class T>
struct divides : binary_function<T, T, T>
{
  divides() {}

  T operator()(T const& x, T const& y) const
    { return x / y; }
};

template<class T>
struct modulus : binary_function<T, T, T>
{
  modulus() {}

  T operator()(T const& x, T const& y) const
    { return x % y; }
};

template<class T>
struct negate : unary_function<T, T>
{
  negate() {}

  T operator()(T const& x) const
    { return -x; }
};


// comparators

template<class T>
struct equal_to : binary_function<T, T, bool>
{
  equal_to() {}

  bool operator()(T const& x, T const& y) const
    { return x == y; }
};

template<class T>
struct not_equal_to : binary_function<T, T, bool>
{
  not_equal_to() {}

  bool operator()(T const& x, T const& y) const
    { return x != y; }
};

template<class T>
struct greater : binary_function<T, T, bool>
{
  greater() {}

  bool operator()(T const& x, T const& y) const
    { return x > y; }
};

template<class T>
struct less : binary_function<T, T, bool>
{
  less() {}

  bool operator()(T const& x, T const& y) const
    { return x < y; }
};

template<class T>
struct greater_equal : binary_function<T, T, bool>
{
  greater_equal() {}

  bool operator()(T const& x, T const& y) const
    { return x >= y; }
};

template<class T>
struct less_equal : binary_function<T, T, bool>
{
  less_equal() {}

  bool operator()(T const& x, T const& y) const
    { return x <= y; }
};


// logical operators

template<class T>
struct logical_and : binary_function<T, T, bool>
{
  logical_and() {}

  bool operator()(T const& x, T const& y) const
    { return x && y; }
};

template<class T>
struct logical_or : binary_function<T, T, bool>
{
  logical_or() {}

  bool operator()(T const& x, T const& y) const
    { return x || y; }
};

template<class T>
struct logical_not : unary_function<T, bool>
{
  logical_not() {}

  bool operator()(T const& x) const
    { return !x; }
};


// function object adaptors

template<class Predicate, class Arg>
class unary_negate : public unary_function<Arg, bool>
{
  public:

    unary_negate(Predicate const& x)
      : pred(x)
      {}

    bool operator()(Arg const& x) const
      { return !pred(x); }

  protected:

    Predicate pred;
};

#define TEMPLATE_not1(Predicate, Arg)                                            \
inline unary_negate<Predicate, Arg> not1(Predicate const& x)                     \
{                                                                                \
  return unary_negate<Predicate, Arg>(x);                                        \
}

template<class Predicate, class Arg1, class Arg2>
class binary_negate : public binary_function<Arg1, Arg2, bool>
{
  public:

    binary_negate(Predicate const& x)
      : pred(x)
      {}

    bool operator()(Arg1 const& x, Arg2 const& y) const
      { return !pred(x, y); }

  protected:

    Predicate pred;
};

#define TEMPLATE_not2(Predicate, Arg1, Arg2)                                     \
inline binary_negate<Predicate, Arg1, Arg2> not2(Predicate const& x)             \
{                                                                                \
  return binary_negate<Predicate, Arg1, Arg2>(x);                                \
}

template<class Operation, class Arg1, class Arg2, class Result>
class binder1st : public unary_function<Arg2, Result>
{
  public:

    binder1st(Operation const& x, Arg1 const& y)
      : op(x), value(y)
      {}

    Result operator()(Arg2 const& x) const
      { return op(value, x); }

  protected:

    Operation op;
    Arg1 value;
};

#define TEMPLATE_bind1st(Operation, Arg1, Arg2, Result)                                       \
inline binder1st<Operation, Arg1, Arg2, Result> bind1st(Operation const& x, Arg1 const& y)    \
{                                                                                             \
  return binder1st<Operation, Arg1, Arg2, Result>(x, y);                                      \
}

template<class Operation, class Arg1, class Arg2, class Result>
class binder2nd : public unary_function<Arg1, Result>
{
  public:

    binder2nd(Operation const& x, Arg2 const& y)
      : op(x),
        value(y)
      {}

    Result operator()(Arg1 const& x) const
      { return op(x, value); }

  protected:

    Operation op;
    Arg2 value;
};

#define TEMPLATE_bind2nd(Operation, Arg1, Arg2, Result)                                       \
inline binder2nd<Operation, Arg1, Arg2, Result> bind2nd(Operation const& x, Arg2 const& y)    \
{                                                                                             \
  return binder2nd<Operation, Arg1, Arg2, Result>(x, y);                                      \
}

template<class Operation1, class Operation2, class ArgOp2, class ResultOp1>
class unary_compose : public unary_function<ArgOp2, ResultOp1>
{
  public:

    unary_compose(Operation1 const& x, Operation2 const& y)
      : op1(x),
        op2(y)
      {}

    ResultOp1 operator()(ArgOp2 const& x) const
      { return op1(op2(x)); }

  protected:

    Operation1 op1;
    Operation2 op2;
};

#define TEMPLATE_compose1(Operation1, Operation2, ArgOp2, ResultOp1)                          \
inline unary_compose<Operation1, Operation2, ArgOp2, ResultOp1>                               \
  compose1(Operation1 const& x, Operation2 const& y)                                          \
{                                                                                             \
  return unary_compose<Operation1, Operation2, ArgOp2, ResultOp1>(x, y);                      \
}

template<class Operation1, class Operation2, class Operation3, class ArgOp2, class ResultOp1>
class binary_compose : public unary_function<ArgOp2, ResultOp1>
{
  public:

    binary_compose(Operation1 const& x, Operation2 const& y, Operation3 const& z)
      : op1(x),
        op2(y),
        op3(z)
      {}

    ResultOp1 operator()(ArgOp2 const& x) const
      { return op1(op2(x), op3(x)); }

  protected:

    Operation1 op1;
    Operation2 op2;
    Operation3 op3;
};

#define TEMPLATE_compose2(Operation1, Operation2, Operation3, ArgOp2, ResultOp1)              \
inline binary_compose<Operation1, Operation2, Operation3, ArgOp2, ResultOp1>                  \
  compose2(Operation1 const& x, Operation2 const& y, Operation3 const& z)                     \
{                                                                                             \
  return binary_compose<Operation1, Operation2, Operation3, ArgOp2, ResultOp1>(x, y, z);      \
}

// adaptors for pointers to functions

template<class Arg, class Result>
class pointer_to_unary_function : public unary_function<Arg, Result>
{
  public:

    pointer_to_unary_function()
      {}

    pointer_to_unary_function(Result (*x)(Arg))
      : ptr(x)
      {}

    Result operator()(Arg x) const
      { return ((pointer_to_unary_function<Arg, Result>*)this)->ptr(x); }

  protected:

    Result (*ptr)(Arg);
};

#ifndef PROBLEM_PTR_FUN

template<class Arg, class Result>
inline pointer_to_unary_function<Arg, Result> ptr_fun(Result (*x)(Arg))
{
  return pointer_to_unary_function<Arg, Result>(x);
}

#else

// instantiate manually

#define TEMPLATE_ptr_fun_unary(Arg, Result)                                             \
inline pointer_to_unary_function<Arg, Result> ptr_fun(Result (*x)(Arg))                 \
{                                                                                       \
  return pointer_to_unary_function<Arg, Result>(x);                                     \
}

#endif

template<class Arg1, class Arg2, class Result>
class pointer_to_binary_function : public binary_function<Arg1, Arg2, Result>
{
  public:

    pointer_to_binary_function()
      {}

    pointer_to_binary_function(Result (*x)(Arg1, Arg2))
      : ptr(x)
      {}

    Result operator()(Arg1 x, Arg2 y) const
      { return ((pointer_to_binary_function<Arg1, Arg2, Result>*)this)->ptr(x, y); }

  protected:

    Result (*ptr)(Arg1, Arg2);
};

#ifndef PROBLEM_PTR_FUN

template<class Arg1, class Arg2, class Result>
inline pointer_to_binary_function<Arg1, Arg2, Result> ptr_fun(Result (*x)(Arg1, Arg2))
{
  return pointer_to_binary_function<Arg1, Arg2, Result>(x);
}

#else

// instantiate manually

#define TEMPLATE_ptr_fun_binary(Arg1, Arg2, Result)                                     \
inline pointer_to_binary_function<Arg1, Arg2, Result> ptr_fun(Result (*x)(Arg1, Arg2))  \
{                                                                                       \
  return pointer_to_binary_function<Arg1, Arg2, Result>(x);                             \
}

#endif


// adaptors for pointers to members

template<class S, class T, class MEMFN>
class mem_fun_t : public unary_function<T*, S>
{
  public:

    mem_fun_t(MEMFN mfn)
      : mfn_(mfn)
      {}

    S operator()(T* p)
      { return (p->*mfn_)(); }

  private:

    MEMFN mfn_;
};


template<class S, class T, class A, class MEMFN>
class mem_fun1_t : public binary_function<T*, A, S>
{
  public:

    mem_fun1_t(MEMFN mfn)
      : mfn_(mfn)
      {}

    S operator()(T* p, A x)
      { return (p->*mfn_)(x); }

  private:

    MEMFN mfn_;
};


#ifndef PROBLEM_MEM_FUN

template<class S, class T>
inline mem_fun_t<S, T> mem_fun(S (T::*f)())
{
  return f;
};

template<class S, class T, class A>
inline mem_fun1_t<S, T, A> mem_fun1(S (T::*f)(A))
{
  return f;
}

#else

// instantiate manually

#define TEMPLATE_mem_fun(S, T)                                          \
inline mem_fun_t<S, T, S (T::*)()> mem_fun(S (T::*f)())                 \
{                                                                       \
  return f;                                                             \
}

#define TEMPLATE_mem_fun1(S, T, A)                                      \
inline mem_fun1_t<S, T, A, S (T::*)(A)> mem_fun1(S (T::*f)(A))          \
{                                                                       \
  return f;                                                             \
}

#endif


template<class S, class T, class MEMFN>
class mem_fun_ref_t : public unary_function<T, S>
{
  public:

    mem_fun_ref_t(MEMFN mfn)
      : mfn_(mfn)
      {}

    S operator()(T& p)
      { return (p.*mfn_)(); }

  private:

    MEMFN mfn_;
};


template<class S, class T, class A, class MEMFN>
class mem_fun1_ref_t : public binary_function<T, A, S>
{
  public:

    mem_fun1_ref_t(MEMFN mfn)
      : mfn_(mfn)
      {}

    S operator()(T& p, A x)
      { return (p.*mfn_)(x); }

  private:

    MEMFN mfn_;
};

#ifndef PROBLEM_MEM_FUN

template<class S, class T>
inline mem_fun_t<S, T> mem_fun(S (T::*f)())
{
  return f;
};

template<class S, class T, class A>
inline mem_fun1_t<S, T, A> mem_fun1(S (T::*f)(A))
{
  return f;
}

#else

// instantiate manually

#define TEMPLATE_mem_fun_ref(S, T)                                      \
inline mem_fun_ref_t<S, T, S (T::*)()> mem_fun_ref(S (T::*f)())         \
{                                                                       \
  return f;                                                             \
}

#define TEMPLATE_mem_fun1_ref(S, T, A)                                  \
inline mem_fun1_ref_t<S, T, A, S (T::*)(A)> mem_fun1_ref(S (T::*f)(A))  \
{                                                                       \
  return f;                                                             \
}

#endif

#endif
